extends ../templates/main.jade

block title
  | C++ | Language Criticism | xigoi

block content
  h1 C++ Language Criticism
  p
    | Everything that applies to 
    a(href="c.html") C
    |  also applies to C++. This article contains only things that don't apply to C.
  p
    | The 
    a(href="http://yosefk.com/c++fqa/index.html") C++ FQA
    |  has a lot of great points, so go read it too.
  h2 Overall philosophy
  p I believe that a programming language should be designed to make simple things simple and complex things as simple as possible. C++ is designed to make simple things complex and complex things even more complex. For example, a program to read space-separated numbers from STDIN, sort them and again output them space-separated:
  .comparison
    .compared
      h6 Python 3
      pre
        code.
          nums = [int(inp) for inp in input().split()]
          print(*sorted(nums))
      a(href="https://tio.run/##K6gsycjPM/7/P680t1jBViE6M69EIzOvQFMhLb9IAcgAYhBVWqKhqVdckJMJpGO5CopAyrSK84tKUlM0QFo1Nf//NzY0UTA0VbBUMDJTMFUwNgUA") Try it online!
    .compared
      h6 Nim
      pre
        code.
          import std/[strutils, sequtils, algorithm]

          let nums = stdin.readLine.splitWhitespace.mapIt(it.parseInt)
          echo nums.sorted.mapIt($it).join(" ")
      a(href="https://tio.run/##LY3LCsIwEADv@YqleGhBIrVG6MEPKHj3IB5Cs9iVvMxuvz8@6G0OM0ykUCuFnIoAizvcWcoq5HkPjO@NrH@mQrKEh1IeBeIaGC4/n6IuaN2VImrOnuS2kCBnO6MONk/SkuhsC@MUpVM4L@lfa/4O0W3OjqTTr0SxbaDpah36E/QGRjiewcBgPg") Try it online!
    .compared
      h6 Haskell
      pre
        code.
          import Data.List

          main = interact $ (++ "\n") . unwords . map show
                          . sort
                          . map (read :: String -> Int) . words
      a(href="https://tio.run/##bcwxC8IwFATgPb/iKA6VYqDWCBZ0chHcXF0eNthg81KSJ/35sXUUbzoO7uspveww5Oz8GKLgTEL66pIo5ckxjnAsNtJDsEJZVSjuXKyh8eYpxC7NzdOI1IdJ4ScaaSb/zMujjJY6tC1uEh0/sTnhwrLIXzfnpt6hNjhgu4dBYz4") Try it online!
    .compared
      h6 C++
      pre
        code.
          #include &lt;iostream&gt;
          #include &lt;vector&gt;
          #include &lt;algorithm&gt;

          int main() {
            std::vector&lt;int&gt; nums;
            std::string inp;
            while (std::getline(std::cin, inp, ' ')) {
              nums.emplace_back(std::stoi(inp));
            }
            std::sort(nums.begin(), nums.end());
            bool first = true;
            for (int num : nums) {
              if (first) {
                first = false;
              } else {
                std::cout &lt;&lt; ' ';
              }
              std::cout &lt;&lt; num;
            }
            std::cout &lt;&lt; std::endl;
          }
      a(href="https://tio.run/##ZY/NTsMwEITveYqRONSRAlIpQSINfRXkOE66wrEj24ED6rMH2/kBxM2e@WZ2V4zjfS/EPN@RFmpqJWoyzlvJh0v2o31I4Y39rXDVG0v@GrCMtMfASbMcXxngfFtVS6IO1gV6Gtx5M0I56R6kxyh9XklJsOT00ivScvkI0kWEChxwyJdipKYHOYyKC/nWcPHO1k5DLMB5Hjtv@yhjPUuRRvZxvWIt0C1b0MYYhY6s83iFt5OMYmcsWLwpwKhSZJtPHVjCNwF7uuPKpXhYADK8d2K5x0wedR2vWaHsnxcm/dl/09MnLK3O2W2eT8cnHEu84PEZJU7lNw") Try it online!
  h2 Features
  ul
    li Despite being a very large language, C++ doesn't have basic features like sum types or pattern matching.
    li Many features duplicate other features, adding their own advantages and drawbacks (and more unnecessary syntactic rules to learn). Examples: initializer lists, 
      code typedef
      | /
      code using
      | , 
      code #define
      | /
      code constexpr
      | , 
      code char[]
      | /
      code std::string
      | , 
      code int[]
      | /
      code std::array&lt;int&gt;
      | , 
      code printf
      | /
      code cout
      | , 
      code struct
      | /
      code class
  h2 Syntax
  p C++ took the horrible syntax of C and somehow managed to make it even worse.
  ul
    li The 
      a(href="https://en.wikipedia.org/wiki/Most_vexing_parse") most vexing parse
      |  (and similar rules). I can't fathom what could possibly cause someone to think this is a good idea.
    li
      a(href="https://blog.reverberate.org/2013/08/parsing-c-is-literally-undecidable.html") Parsing C++ is literally undecidable.
    li Using less-than and greater-than signs as brackets, which hinders auto-pairing and complicates parsing while also looking ugly. 
      a(href="https://soc.me/languages/stop-using-for-generics") Here's a longer explanation from another person.
    li The semicolon after a 
      code struct
      | /
      code class
      |  definition. Just why?
    li Keywords that have multiple meanings depending on where you use them: 
      code static
      | , 
      code using
      | , 
      code typename
      | .
    li The keyword 
      code const
      |  is used to declare immutable variables (not constants, for which there is 
      code constexpr
      | ) and it can be placed in various positions inside a type, which completely changes its meaning.
    li Who decided that 
      code ::
      |  is a good path separator?
  h2 Standard library
  ul
    li Despite being quite extensive, the standard library doesn't have basic things like
      ul
        li basic functions for working with strings (split, join)
        li functional abstractions (map, filter, fold, …) [C++20 partially solves this with “range adaptors” (what a weird name), but the usage is unbearably cumbersome]
        li optional/result types
    li Using the term “vector” for a resizable array, even though the word has a completely different meaning in mathematics.
    li There is no convenient way to pass a whole array/vector/list to a function, you have to do something like 
      code std::sort(arr.begin(), arr.end())
      | .
    li Weird function names: from C crypticisms (
      code stoi
      | , 
      code fscanf
      | ) to unusual words (
      code emplace_back
      | ).
    li Idiomatically, it's required to write 
      code std::
      |  between everything from the standard library, which makes the code repetitive and unreadable. Who would have guessed that 
      code sort
      |  is a standard library function if it wasn't for the prefix?
  h2 Tooling
  ul
    li Error messages.
      ul
        li If you try to search a “vector” (array) of a wrong type…
          p Credit goes to 
            a(href="https://codegolf.stackexchange.com/a/10470/98955") this StackExchange answer
            | .
          pre
            code.
              #include &lt;vector&gt;
              #include &lt;algorithm&gt;

              int main() {
                  int a;
                  std::vector&lt;std::vector&lt;int&gt;&gt; v;
                  std::vector&lt;std::vector&lt;int&gt;&gt;::const_iterator it = std::find(v.begin(), v.end(), a);
              }
          a(href="https://tio.run/##jY1BDoIwEEX3PcUkbiBRDlCwVzF1OtZJoCVl7MZ49lJgoUv/6uXl/3yc54tHLOXEAceXIxgyocRk1NfY0cfE8pyMUhwEJsuhaeGtoGYTtt9xEaf1MR9@uVaMgfxHSWuMYZEbCyVbLbDA9Zg8OLgmd3fy2/kZckdVVLBtrz6lrA") Try it online!
          p The error message (compiled with GCC 10.2.0) has 148 lines with about 12 kB of content, starting with:
          pre
            samp.
              In file included from /usr/include/c++/10.2.0/bits/stl_algobase.h:71,
                               from /usr/include/c++/10.2.0/vector:60,
                               from error.cpp:1:
              /usr/include/c++/10.2.0/bits/predefined_ops.h: In instantiation of ‘bool __gnu_cxx::__ops::_Iter_equals_val&lt;lt;_Value&gt;::operator()(_Iterator) [with _Iterator = __gnu_cxx::__normal_iterator&lt;lt;std::vector&lt;lt;int&gt;*, std::vector&lt;lt;std::vector&lt;lt;int&gt; &gt; &gt;; _Value = const int]’:
              /usr/include/c++/10.2.0/bits/stl_algobase.h:1932:14:   required from ‘_RandomAccessIterator std::__find_if(_RandomAccessIterator, _RandomAccessIterator, _Predicate, std::random_access_iterator_tag) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator&lt;lt;std::vector&lt;lt;int&gt;*, std::vector&lt;lt;std::vector&lt;lt;int&gt; &gt; &gt;; _Predicate = __gnu_cxx::__ops::_Iter_equals_val&lt;lt;const int&gt;]’
              /usr/include/c++/10.2.0/bits/stl_algobase.h:1977:23:   required from ‘_Iterator std::__find_if(_Iterator, _Iterator, _Predicate) [with _Iterator = __gnu_cxx::__normal_iterator&lt;lt;std::vector&lt;lt;int&gt;*, std::vector&lt;lt;std::vector&lt;lt;int&gt; &gt; &gt;; _Predicate = __gnu_cxx::__ops::_Iter_equals_val&lt;lt;const int&gt;]’
              /usr/include/c++/10.2.0/bits/stl_algo.h:3902:28:   required from ‘_IIter std::find(_IIter, _IIter, const _Tp&) [with _IIter = __gnu_cxx::__normal_iterator&lt;lt;std::vector&lt;lt;int&gt;*, std::vector&lt;lt;std::vector&lt;lt;int&gt; &gt; &gt;; _Tp = int]’
              error.cpp:7:87:   required from here
              /usr/include/c++/10.2.0/bits/predefined_ops.h:268:17: error: no match for ‘operator==’ (operand types are ‘std::vector&lt;lt;int&gt;’ and ‘const int’)
                268 |  { return *__it == _M_value; }
                    |           ~~~~~~^~~~~~~~~~~
              In file included from /usr/include/c++/10.2.0/bits/stl_algobase.h:67,
                               from /usr/include/c++/10.2.0/vector:60,
                               from error.cpp:1:
              /usr/include/c++/10.2.0/bits/stl_iterator.h:1064:5: note: candidate: ‘template&lt;lt;class _IteratorL, class _IteratorR, class _Container&gt; bool __gnu_cxx::operator==(const __gnu_cxx::__normal_iterator&lt;lt;_IteratorL, _Container&gt;&, const __gnu_cxx::__normal_iterator&lt;lt;_IteratorR, _Container&gt;&)’
               1064 |     operator==(const __normal_iterator&lt;lt;_IteratorL, _Container&gt;& __lhs,
                    |     ^~~~~~~~
              /usr/include/c++/10.2.0/bits/stl_iterator.h:1064:5: note:   template argument deduction/substitution failed:
        li If you forget a semicolon…
          p Credit goes to 
            a(href="https://codegolf.stackexchange.com/a/205268/98955") this StackExchange answer
            | .
          pre
            code.
              constexpr double pi = 3.0 // An engineer approximation

              #include &lt;iostream&gt;

              int main() {
                std::cout &lt;&lt; "Enter the radius: " &lt;&lt; std::flush;
                double radius;
                std::cin &gt;&gt; radius;
                std::cout &lt;&lt; "Area: " &lt;&lt; pi * radius * radius &lt;&lt; std::endl;
              }
          a(href="https://tio.run/##ZY7NCsIwEITveYpBLypoBW9tLXjwQWKy6kK7CfkBQXz2WrXVg7dl@PabMd6vL8b0vXESE918gHX51BI8Y4/dZouiwEFAcmEhCtDeB3fjTid2otScxbTZEmp2MQXSXaMUS0KnWRZL3BUQky1L43JCXWN2lDRo0pUQtOUcS8xe@Rs6tzleq@FlHPEhqq@DBU3zl07mw1A/2ob1q5H7HVMLiW0r9ej7Jw") Try it online!
          p The error message (compiled with GCC 10.2.0) has 3244 lines with about 250 kB of content, starting with:
          pre
            samp.
              In file included from /usr/include/c++/10.2.0/iostream:38,
                from error.cpp:3:
              /usr/include/c++/10.2.0/x86_64-pc-linux-gnu/bits/c++config.h:258:1: error: expected ‘,’ or ‘;’ before ‘namespace’
                258 | namespace std
                    | ^~~~~~~~~
              In file included from /usr/include/c++/10.2.0/iosfwd:40,
                from /usr/include/c++/10.2.0/ios:38,
                  from /usr/include/c++/10.2.0/ostream:38,
                    from /usr/include/c++/10.2.0/iostream:39,
                      from error.cpp:3:
              /usr/include/c++/10.2.0/bits/postypes.h:98:11: error: ‘ptrdiff_t’ does not name a type
                98 |   typedef ptrdiff_t streamsize; // Signed integral type
                   |           ^~~~~~~~~
              /usr/include/c++/10.2.0/bits/postypes.h:41:1: note: ‘ptrdiff_t’ is defined in header ‘&lt;cstddef&gt;’; did you forget to ‘#include &lt;cstddef&gt;’?
                40 | #include &lt;cwchar&gt; // For mbstate_t
               +++ |+#include &lt;cstddef&gt;
                41 | 
              In file included from /usr/include/c++/10.2.0/bits/exception_ptr.h:40,
                from /usr/include/c++/10.2.0/exception:147,
                  from /usr/include/c++/10.2.0/ios:39,
                    from /usr/include/c++/10.2.0/ostream:38,
                      from /usr/include/c++/10.2.0/iostream:39,
                        from error.cpp:3:
              /usr/include/c++/10.2.0/new:126:26: error: declaration of ‘operator new’ as non-function
                126 | _GLIBCXX_NODISCARD void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
                    |                          ^~~~~~~~
              /usr/include/c++/10.2.0/new:126:44: error: ‘size_t’ is not a member of ‘std’; did you mean ‘size_t’?
                126 | _GLIBCXX_NODISCARD void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
                    |                                            ^~~~~~
          p I guess the fourth line does tell you that you missed a semicolon (if you have the patience to scroll to it), but it's not exactly clear where.
        li Would you prefer an error that I actually encountered? Here is a simplified version of some code I wrote in a programming contest:
          pre
            code.
              #include &lt;algorithm&gt;
              #include &lt;iostream&gt;
              #include &lt;numeric&gt;
              #include &lt;string&gt;
              #include &lt;vector&gt;

              std::string decrypt(std::string& k, std::string& m) {
                std::vector&lt;std::string&gt; c(k.length());
                int n = m.length() % k.length() == 0 ? m.length() / k.length() : m.length() / k.length() + 1;
                int a = 0;
                for (std::string& s : c) {
                  for (int i = 0; i &lt; n; i++) {
                    s += a &gt;= m.length() ? ' ' : m[a];
                    a++;
                  }
                }
                std::vector&lt;int&gt; j(k.length());
                std::iota(j.begin(), j.end(), 0);
                std::sort(j.begin(), j.end(), [&k](int x, int y) { return k[x] &lt; k[y]; });
                std::vector&lt;int&gt; q(k.length());
                for (int i = 0; i &lt; k.length(); i++) {
                  q[j[i]] = i;
                }
                std::string r = "";
                for (int i = 0; i &lt; n; i++) {
                  for (int ii = 0; ii &lt; k.length(); ii++) {
                    char h = c[q[ii]][i];
                    if (c != ' ') {
                      r += c;
                    }
                  }
                }
                return r;
              }

              int main() {
                std::string k;
                std::getline(std::cin, k);
                std::string m;
                std::getline(std::cin, m);
                std::cout &lt;&lt; decrypt(k, m) &lt;&lt; std::endl;
              }
          a(href="https://tio.run/##fZPbboMwDIbveQqv0zYQrOtuy6EPgrhgaQrhENoQplYTz87MqQldOyFE8P/F/LYDOR7fE0K67plxUjR7Cl5cJJVgMi0DQwVZVUtB40WMNyUVjOghhBhP9Mg3JbISgWHUcr/djjrsKRGXozS12CvkDizeSwt@DBhjYxZP0wMgZr4uKE9kalqWiyTjEjj4UF7D8AKKAd@HDex0@UOXtw8VGz7n/DHm3/Qvh0rA0n@NGcjoeZL7DWzYgA8POD5seyawMrB9TBgsHO/gDS/0EsaRO4GxbY/L1hhvvSf4kQCy214MBKtkbGbrL5owbloOZGvK9/1io5i6EvIuE77m0VDB2Rkqv6BxEFQ2gkMeniMsKA8vkQutSqZbOt1autcShSx6cwqzkEURcszVS56Oj0BhtXqUc9lmRczIn@8uhkLSWECKKAlPIUMTaGSeAzuASeDJ70ekdgD6wUGSmWoXo5o6JlyjNYzeRxn3jVZHe6opvzYxobJgnI6HizDuQK6Na6TLf@hS0aRqJHje9X/Le7UPDCoOukBbXfcL") Try it online!
          p The error message does tell you where the error is, but you have to scroll through 228 lines with over 22 kB of content.
